#include <chrono>
#include <filesystem>
#include <fstream>
#include <thread>
#include "file_output_strategy.h"
#include "gtest/gtest.h"

namespace Base {
namespace SystemLog {
namespace {
constexpr const char* TEST_LOG_DIR = "out/tests/logs";
constexpr const char* TEST_MESSAGE = "Hello, File Output!\n";
constexpr const char TEST_CHAR = 't';
constexpr const size_t TEST_MAX_FILE_SIZE = 1024; // 1KB
constexpr const size_t TEST_OVER_FILE_SIZE = 1536; // 1.5KB

std::string ReadFileContent(const std::string &path) {
    std::ifstream file(path);
    if (!file.is_open()) {
        return "";
    }
    return std::string((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
}
} // namespace

class FileOutputStrategyTest : public testing::Test {
public:
    static void SetUpTestCase(void);
    static void TearDownTestCase(void);
    void SetUp();
    void TearDown();
};

void FileOutputStrategyTest::SetUpTestCase(void)
{}

void FileOutputStrategyTest::TearDownTestCase(void)
{}

void FileOutputStrategyTest::SetUp(void)
{
    std::filesystem::create_directories(TEST_LOG_DIR);
}

void FileOutputStrategyTest::TearDown(void)
{
    try {
        if (std::filesystem::exists(TEST_LOG_DIR)) {
            std::filesystem::remove_all(TEST_LOG_DIR);
        } else {
            GTEST_LOG_(ERROR) << "directory does not exist: " << TEST_LOG_DIR << '\n';
        }
    } catch (const std::filesystem::filesystem_error &ex) {
        GTEST_LOG_(ERROR) << "deletion failed: " << ex.what() << '\n';
    }
}

/**
 * @tc.name: FileOutputStrategyTest_001
 * @tc.desc: verify Write function.
 * @tc.type: FUNC
 */
TEST_F(FileOutputStrategyTest, FileOutputStrategyTest_001)
{
    FileOutputOptions options;
    options.filePathPattern = std::string(TEST_LOG_DIR) + "/file_output_strategy_test.log";
    FileOutputStrategy file(options);
    ASSERT_EQ(file.Write(TEST_MESSAGE, strlen(TEST_MESSAGE)), ERR_OK);
    ASSERT_EQ(file.Flush(), ERR_OK);

    auto content = ReadFileContent(options.filePathPattern);
    ASSERT_EQ(content, TEST_MESSAGE);
}

/**
 * @tc.name: FileOutputStrategyTest_002
 * @tc.desc: verify file rolling function.
 * @tc.type: FUNC
 */
TEST_F(FileOutputStrategyTest, FileOutputStrategyTest_002) {
    FileOutputOptions options;
    options.filePathPattern = std::string(TEST_LOG_DIR) + "/rolling_size_%Y%m%d_%H%M%S.log";
    options.maxFileSize = TEST_MAX_FILE_SIZE; // 1KB

    FileOutputStrategy file(options);
    
    // 写入超过限制的数据
    std::string data(TEST_OVER_FILE_SIZE, TEST_CHAR);
    ASSERT_EQ(file.Write(data.data(), data.size()), ERR_OK);
    std::this_thread::sleep_for(std::chrono::seconds(1));
    ASSERT_EQ(file.Write(data.data(), data.size()), ERR_OK);
    ASSERT_EQ(file.Close(), ERR_OK);

    // 检查文件数量
    int32_t fileCount = 0;
    for (const auto& entry : std::filesystem::directory_iterator(TEST_LOG_DIR)) {
        if (entry.path().string().find("rolling_size") != std::string::npos) {
            ++fileCount;
            auto size = std::filesystem::file_size(entry.path());
            ASSERT_LE(size, TEST_MAX_FILE_SIZE + TEST_OVER_FILE_SIZE); // 原始大小+新数据
        }
    }
    ASSERT_EQ(fileCount, 2); // 会滚动一次
}
} // namespace SystemLog
} // namespace Base
